In this second part of our tutorial series “Simple WordPress Advertising Plugin” we will learn how to display our ads, add additional information and even schedule the ending of our ads. We will also use some simple caching so that our ads are retrieved much faster.
This tutorial is a part of a tutorial series “Simple WordPress Advertising Plugin“. The series consists of several tutorials which are:
- The Introduction – Create the base of the plugin, create Ads in the admin area and more
- Displaying Ads – Displaying Ads on the front page, caching them on a daily basis and more
- Tracking Ads – tracking Ad impressions and clicks, calculating CTR (click-through-rate)
- Widget and Shortcode – creating a simple widget and a shortcode to display the Ads in sidebar or content
This will be a little longer that the last tutorial so be ready for some great learning. We will start by editing our main class.
If you are eager to read the code rather then reading, then you can download the code here.
Editing the Main Class
We will add some new attributes and methods. Since our main class Simple_WP_Ads is a singleton instance, only one of object will be available and everything in that object can be used through the whole WordPress life cycle when our page loads.
Due to that, we will store ads in that class so that we can easily display them on the top and on the bottom of the page. Before we get into displaying them, let’s define our new attributes and methods. We will also need to edit some of the methods previously defined.
At the top of our main class add this:
<?php | |
// simple-wp-ads.php | |
/** | |
* Main Class | |
*/ | |
class Simple_WP_Ads { | |
/** | |
* Ads to be rendered | |
* @var array | |
*/ | |
protected $ads = array(); | |
/** | |
* Return ads | |
* @return mixed | |
*/ | |
public function get_ads(){ | |
return $this->ads; | |
} | |
/** | |
* Sets ads | |
* @param mixed $ads | |
*/ | |
public function set_ads( $ads ){ | |
$this->ads = $ads; | |
} | |
/** | |
* Check if any ad is in position | |
* @param string $position the position to check | |
* @return mixed | |
*/ | |
public function has_ad_position( $position ){ | |
if( ! is_array( $this->ads ) ) { | |
return false; | |
} | |
if( isset( $this->ads[ $position ] ) && count( $this->ads[ $position ] ) > 0 ) { | |
return true; | |
} | |
return false; | |
} | |
// ... | |
} |
The attribute $ads
will store our ads. We have two mutator methods get_ads
and set_ads
. They are used to change the value of our attribute $ads
.
There is another method has_ad_position
. This method will check our attribute $ads
for the provided position and return true
or false
. We have defined this method because we will have two positions for ads: top and bottom.
We will store our ads in the attribute $ads
based on their positions. So the actual array will have to look something like this:
<?php | |
array( | |
// Storing ads for top position | |
'top' => array( | |
0 => array( | |
//Ads parameters | |
) | |
), | |
// Storing ads for bottom position | |
'bottom' => array( | |
0 => array( | |
//Ads parameters | |
) | |
) | |
); |
New Dependencies
For our new functionality we will have to create new files. We will just create them for now and populate through this article. In the folder simple_wp_ads/inc
create two new files:
- swa-functions.php
- swa-front.php
We have to add those dependencies in our method load_dependencies
:
<?php | |
// simple-wp-ads.php | |
// ... | |
// Simple_WP_Ads | |
/** | |
* Load dependencies | |
* @return void | |
*/ | |
public function load_dependencies(){ | |
/** | |
* Abstracts | |
*/ | |
require_once SWA_ROOT .'inc/abstracts/class-swa-settings.php'; | |
/** | |
* Classes | |
*/ | |
require_once SWA_ROOT .'inc/class-swa-status.php'; | |
/** | |
* Base functions | |
*/ | |
require_once SWA_ROOT .'inc/swa-functions.php'; | |
require_once SWA_ROOT .'inc/swa-cpt.php'; | |
require_once SWA_ROOT .'inc/swa-statuses.php'; | |
/** | |
* Load only on admin side | |
*/ | |
if( is_admin() ) { | |
require_once SWA_ROOT .'inc/class-swa-metabox.php'; | |
} | |
/** | |
* We are on the front page | |
*/ | |
if( ! is_admin() ) { | |
require_once SWA_ROOT .'inc/swa-front.php'; | |
} | |
} | |
// ... |
If you copied the code, you may have notices that we removed the two add_action
hooks. We will still use them, but in another method.
Running Simple WordPress Advertising Plugin
I have called this new method run
becuase everything needed for our plugin will be called here. This method will be used for every possible hook we will have to use.
<?php | |
// simple-wp-ads.php | |
// ... | |
// Simple_WP_Ads | |
/** | |
* Run the plugin | |
* @return void | |
*/ | |
public function run(){ | |
// Hook into the 'init' action | |
add_action( 'init', 'swa_cpt', 0 ); | |
// Hook when swa has ended | |
add_action( 'swa_ad_ended', 'swa_ad_ended' ); | |
if( is_admin() ) { | |
add_action( 'load-post.php', array( $this, 'construct_metabox_on_ad' ) ); | |
add_action( 'load-post-new.php', array( $this, 'construct_metabox_on_ad' ) ); | |
add_action( 'save_post', 'swa_save_ad', 99, 2 ); | |
} | |
if( ! is_admin() ) { | |
// Add Style on front end | |
add_action( 'wp_head', 'swa_inline_style' ); | |
// Get banners | |
add_action( 'init', 'swa_get_ads', 20 ); | |
// Add body clases used in inline styles | |
add_action( 'init', 'swa_add_body_classes', 30 ); | |
// Display the ads | |
add_action( 'wp_footer', 'swa_display_ads', 30 ); | |
} | |
} | |
// ... |
The first action hook is the one we have defined in the previous article. This hook was placed in our file inc/swa-cpt.php
, so please open that file and remove the hook at the bottom.
The next hook is swa_ad_ended
and this hook will call the function with the same name. This hook is our own hook which we will define in our scheduling part. Inside is_admin
we are adding the action hooks which we have previously removed from the load_dependencies
. We are also hooking our new function swa_save_ad
to the action save_post
.
This function will do the scheduling part. When we are not in the admin area, we will:
- add our inline style using
wp_head
hook - get our ads before even rendering our page using
init
hook - add new body classes based on our ads
- render the ads on the bottom of the page using
wp_footer
hook
We have used the init
hook twice but the important difference is the priority. With priority we have defined that we will first get the ads from the cache or database (priority: 20
) and only after that we will define our body classes (priority: 30
).
We had to do that because we need to be sure we have our attribute $ads
populated before we begin adding new body classes which are based on our ads.
All the functions added to the mentioned hooks will be defined in our new files.
Scheduling Ads
We will schedule our ads using the function swa_save_ad
. Open the file inc/swa-functions.php
and add this:
<?php | |
/** | |
* Function that triggers when our Ad is saved | |
* In this function we schedule the end of this ad to clear the transient and set the status of Ad | |
* @param number $post_id | |
* @param object $post | |
* @return mixed | |
*/ | |
function swa_save_ad( $post_id, $post ){ | |
$slug = 'swa'; | |
if ( $slug != $post->post_type ) { | |
return; | |
} | |
$format = 'd-m-Y H:i'; | |
$post_date = get_the_date( $format, $post_id ); | |
$datetime = DateTime::createFromFormat( $format, $post_date ); | |
$timestamp = $datetime->getTimestamp(); | |
// Get the current Server Time | |
$current_time = time(); | |
// Get the Current WordPress time | |
$wp_current_time = current_time( 'timestamp' ); | |
// Get GMT offset | |
$offset = get_option('gmt_offset'); | |
// Get Timezone string | |
$timezone = get_option('timezone_string'); | |
$difference = 0; | |
if( $timezone ) { | |
date_default_timezone_set($timezone); | |
} else { | |
// Get the difference between. Difference will be minus if that time did not happen yet (such as GMT+0 and GMT+6) | |
$difference = $wp_current_time - $current_time; | |
} | |
// Multiply by -1 to get + so NY != -6 --> == +6 | |
$the_end_time = (-1) * $difference + $timestamp; | |
// Get the duration of Ad | |
// Get the duration of Ad | |
$duration_of_ad = 0; | |
if( isset( $_POST['duration'] ) ) { | |
$duration_of_ad = $_POST['duration']; | |
} | |
$duration_of_ad_timestamp = $duration_of_ad * DAY_IN_SECONDS; | |
$the_end_time += $duration_of_ad_timestamp; | |
// Don't schedule if the end time is in the past | |
if( $wp_current_time > $the_end_time ) { | |
return $post_id; | |
} | |
// Post Status is 'Ended', publish it back | |
if( $post->post_status == 'ended' ){ | |
swa_set_status( $post_id, 'swa', 'publish', 'ended' ); | |
} | |
$scheduled_end_of_ad = wp_next_scheduled( 'swa_ad_ended', array( $post_id ) ); | |
// If already scheduled, remove it | |
if( $scheduled_end_of_ad ) { | |
wp_unschedule_event( $scheduled_end_of_ad, 'swa_ad_ended', array( $post_id ) ); | |
} | |
// Schedule the end | |
wp_schedule_single_event( $the_end_time, 'swa_ad_ended', array( $post_id ) ); | |
} |
First, we are checking if we are saving our ad. If not, we are stop processing our scheduling. This may seem a bit complicated but try to stay focused. I am sure you will understand everything.
We are getting the actual timestamp from the date on which our Ad has been published. This will be used so that we can calculate the end timestamp of our Ad.
We are then getting the server time in $current_time
and the WordPress time $wp_current_time
saved in Settings > General. Both are in a timestamp format. These variables will be used if the WordPress time is saved without timezones such as UTC+0, UTC+1 and similar settings.
After that, we are getting the offset which will give us an hourly value such as -1, +2 and similar. After that we are also getting the timezone setting. If there is no timezone, then it means the time and date settings are saved as UTC+0 or similar values.
If that is the case, we need to calculate the difference. Since our server time may be different from the WordPress time, we need to calculate the difference to be accurate when scheduling. If there is a timezone, then we are setting the server time on that timezone so that we are also getting the daylight setting (such can help on summer/winter days when there is a +/- hour transition).
We are then getting the end time of our Ad. $the_end_time
stores the timestamp of our published date. We need to add the duration of our Ad to get the final end time. We are adding the duration by multiplying it with the seconds of 1 day.
If the end time is lower than the current time, it means that we have already pass the ending date and so we will not schedule it.
If we are still to schedule our Ad and our Ad has a status of ended
, we will transition our Ad back to the status publish
. This is done by our custom function swa_set_status
which we will define next.
After that, we are checking if there is a cron job for our Ad. If there is, we are unscheduling it. We need to do that because we do not know if the previous scheduled event for our Ad has the same ending time.
The last step is scheduling the ending of our Ad. When our Ad has ended, the action hook swa_ad_ended
will be called. We have hooked a function swa_ad_ended
to that action in our method run
.
Setting Statuses
To set different statuses, as we have seen in the previous code, we have to update the database row of our Ad. We will do that by using $wpdb
. Add this code to our file inc/swa-functions.php
:
<?php | |
// ... | |
/** | |
* Set status for a post | |
* @param number $post_id ID of the post for which we want to set the status | |
* @param string $post_type Post Type | |
* @param string $status New status we want to set | |
* @param string $from_status Old status, from which we will set | |
* @return boolean True/False | |
*/ | |
function swa_set_status( $post_id, $post_type, $status, $from_status = 'publish'){ | |
if( get_post_type( $post_id ) == $post_type ){ | |
global $wpdb; | |
$update_status = $wpdb->update( | |
$wpdb->posts, | |
array( | |
'post_status' => $status, // string | |
), | |
array( 'ID' => $post_id, 'post_type' => $post_type, 'post_status' => $from_status ), | |
array( | |
'%s', // value1 | |
), | |
array( '%d', '%s', '%s' ) | |
); | |
if( $update_status ){ | |
wp_transition_post_status( $status, $from_status, get_post($post_id) ); | |
return true; | |
} | |
return false; | |
} | |
} |
First we are checking if we are to change the post type passed to that function. If that is true, we are updating the database table posts
where the ID is our passed $post_id
, post type and also the status from which we want to change. If a row has been changed, we are returning true
, instead we will return false
.
Ending the Ad
The last entry in our file inc/swa-functions.php
will be a function that will be triggered when the action hook swa_ad_ended
is called. Add this code:
<?php | |
// ... | |
/** | |
* Function triggered when the ad has ended | |
* @param number $post_id | |
* @return void | |
*/ | |
function swa_ad_ended( $post_id ) { | |
swa_set_status( $post_id, 'swa', 'ended' ); | |
delete_transient( 'swa_ads' ); | |
} |
This is a simple function. We are setting the status of our Ad to ended
and we are also clearing our temporary cache swa_ads
. This cache will be set when we are retrieving Ads.
Ads Metabox Fields
We also need to create some additional information for our Ads. We will have a field for URL where the visitor will get redirected if he/she clicks on the Ad. Another field will also be the position of our Ad. Open the file inc/swa-metaboxes.php
and add this to the bottom:
<?php | |
// ... | |
$swa_metabox->add_field( | |
array( | |
'name' => 'link', | |
'title' => __( 'Link of Ad', 'swa'), | |
'default' => 'http://', | |
'desc' => __( 'User who clicks will be redirected to this link', 'swa' ) | |
) | |
); | |
$swa_metabox->add_field( | |
array( | |
'name' => 'position', | |
'title' => __( 'Position of the Ad', 'swa'), | |
'default' => 'top', | |
'type' => 'select', | |
'options' => array( | |
'top' => __( 'Top', 'swa' ), | |
'bottom' => __( 'Bottom', 'swa' ) | |
) | |
) | |
); |
Now we can get the Ads and display them:)
Getting the Ads
In the action init
we hooked the function swa_get_ads
. Let’s define that function in the file inc/swa-front.php
:
<?php | |
// ... | |
/** | |
* Get Ads from cache or from the database | |
* @return mixed | |
*/ | |
function swa_get_ads(){ | |
if ( false === ( $ads = get_transient( 'swa_ads' ) ) ) { | |
/** | |
* The WordPress Query class. | |
* @link http://codex.wordpress.org/Function_Reference/WP_Query | |
* | |
*/ | |
$args = array( | |
//Type & Status Parameters | |
'post_type' => 'swa', | |
'post_status' => 'publish', | |
//Pagination Parameters | |
'posts_per_page' => 100 | |
); | |
$ads_from_db = get_posts( $args ); | |
$ads = swa_create_ads_array( $ads_from_db ); | |
set_transient( 'swa_ads', $ads ); | |
} | |
$swa = swa(); | |
$swa->set_ads( $ads ); | |
return $ads; | |
} |
We are checking if there are Ads cached. If there is no cache, we are getting them from the database. Once we get the Ads, we are creating the array of Ads with the function swa_create_ads_array
. Once we create it, we save the constructed array in the cache.
The constructed array is also set to our main class using the method set_ads
.
Constructing the Ads array
In the previous method we have also mentioned a new function swa_create_ads_array
. Let’s add the next code:
<?php | |
// ... | |
/** | |
* Create Ads Array we will store and use | |
* @param array $ads Array of posts | |
* @return mixed | |
*/ | |
function swa_create_ads_array( $ads ){ | |
$ads_array = array(); | |
$ads_array['top'] = array(); | |
$ads_array['bottom'] = array(); | |
foreach ( $ads as $ad ) { | |
$ad_meta = get_post_meta( $ad->ID, 'swa_info', true ); | |
$new_ad = array( | |
'id' => $ad->ID, | |
'post_title' => $ad->post_title, | |
'link' => $ad_meta['link'], | |
'image' => get_the_post_thumbnail( $ad->ID, 'full' ) | |
); | |
$position = $ad_meta['position']; | |
$ads_array[ $position ][] = $new_ad; | |
} | |
if( count( $ads_array['top'] ) == 0 && count( $ads_array['bottom'] ) == 0 ) { | |
return false; | |
} | |
return $ads_array; | |
} |
We are first creating an empty array with both top
and bottom
positions. Both of those positions are empty arrays. After that we are going through all our ads which were retrieved from the database.
For each Ad, we are also getting the metadata saved from our metabox. After that, we create a new Ad array to hold the information for that particular Ad. Once we created the array $new_ad
, we push that array to our main Ads array $ads_array
under the position we got from the metadata.
If there are no Ads under both positions, we return false. Otherwise, we return the array.
Displaying the Ads
To display the Ads, we have to set the styles. We will create inline styles so that we save on HTTP requests. Add this to the file inc/swa-front.php
:
<?php | |
/** | |
* Inline Styles in WP Head | |
* @return void | |
*/ | |
function swa_inline_style() { | |
$swa = swa(); | |
if( ! $swa->has_ad_position('top') && ! $swa->has_ad_position('bottom') ) { | |
return; | |
} | |
?> | |
<style> | |
body.swa_ad_top { | |
padding-top:50px; | |
} | |
body.swa_ad_bottom { | |
padding-bottom: 50px; | |
} | |
.swa_ad_top .swa_ad_top { | |
position: absolute; | |
top:0; | |
} | |
.swa_ad_bottom .swa_ad_bottom { | |
position: fixed; | |
bottom:0; | |
top: auto; | |
z-index:999; | |
} | |
.swa_ad { | |
left:0; | |
width: 100%; | |
height: 50px; | |
line-height: 50px; | |
text-align: center; | |
background-color:#333; | |
color:white; | |
overflow: hidden; | |
} | |
.swa_ad img { | |
max-height: 50px; | |
width: auto; | |
} | |
.swa_ad p { | |
display:inline-block; | |
margin-left: 1em; | |
color:white; | |
} | |
.admin-bar.swa_ad_top .swa_ad_top{ | |
top: 32px; | |
} | |
@media screen and (max-width: 782px) { | |
.admin-bar.swa_ad_top .swa_ad{ | |
top: 46px; | |
} | |
} | |
</style> | |
<?php | |
} |
We are first checking if there are any Ads. If there are, only then we will render those styles. You can easily change the styles to your own theme.
As you can see, for our styles and Ads to render correctly, we need to add some body classes. This can be done by using the filter hook body_class
. We have hooked our function swa_add_body_classes
to the action init
. Now, let’s define that function:
<?php | |
/** | |
* Add body classes for position on which ads will be displayed | |
* @return [type] [description] | |
*/ | |
function swa_add_body_classes() { | |
$swa = swa(); | |
if( $swa->has_ad_position( 'top' ) ) { | |
add_filter('body_class', 'swa_add_body_top_class' ); | |
} | |
if( $swa->has_ad_position( 'bottom' ) ) { | |
add_filter('body_class', 'swa_add_body_bottom_class' ); | |
} | |
} | |
/** | |
* Filter function to add the class for top | |
* @param array $classes Array of classes that will be added to body | |
* @return array | |
*/ | |
function swa_add_body_top_class( $classes ) { | |
$classes[] = 'swa_ad_top'; | |
return $classes; | |
} | |
/** | |
* Filter function to add the class for bottom | |
* @param array $classes Array of classes that will be added to body | |
* @return array | |
*/ | |
function swa_add_body_bottom_class( $classes ) { | |
$classes[] = 'swa_ad_bottom'; | |
return $classes; | |
} |
In this function we are checking for Ads under a certain position. If there is one, we are hooking our function(s) to that filter.
The last thing we have to do now is to render the Ads. This is done by the function swa_display_ads
which is hooked in wp_footer
. Add this code:
<?php | |
// ... | |
/** | |
* Display the ads, one random ad per position | |
* @return void | |
*/ | |
function swa_display_ads() { | |
$swa = swa(); | |
$ads = $swa->get_ads(); | |
if( $ads ) { | |
$top_ads = $ads['top']; | |
$bottom_ads = $ads['bottom']; | |
if( count( $top_ads ) > 0 ) { | |
$random_top_ad = array_rand( $top_ads ); | |
swa_render_ad( $top_ads[ $random_top_ad ], 'top' ); | |
} | |
if( count( $bottom_ads ) > 0 ) { | |
$random_bottom_ad = array_rand( $bottom_ads ); | |
swa_render_ad( $bottom_ads[ $random_bottom_ad ], 'bottom' ); | |
} | |
} | |
} | |
/** | |
* Rendering function for the Ad | |
* @param array $ad_array Definition array of Ad | |
* @param string $position | |
* @return void | |
*/ | |
function swa_render_ad( $ad_array, $position ) { | |
$swa_ad_class = 'swa_ad_top'; | |
if( $position == 'bottom' ) { | |
$swa_ad_class = 'swa_ad_bottom'; | |
} | |
$link = $ad_array['link']; | |
if( strpos( $link, 'http' ) === false ) { | |
$link = 'http://' . $link; | |
} | |
echo '<div class="swa_ad ' . $swa_ad_class . '">'; | |
echo '<a target="_blank" href="' . $link . '">'; | |
echo $ad_array['image']; | |
echo '<p>' . $ad_array['post_title'] . '</p>'; | |
echo '</a>'; | |
echo '</div>'; | |
} |
In this function we are getting the Ads which were retrieved from the cache or from the database. If there are any Ads, we are getting Ads for both top
and bottom
. We check if there are Ads in any of those positions and if there are, then we get only one Ad from each position which we then render using the function swa_render_ad
.
You can easily change the rendering HTML to display the Ads in the way you want.
Conclusion
In this tutorial we have created many new functions used to get the Ads, cache them and display them. By using various action and filter hooks, you can extend the front and the back of WordPress.
Using some simple caching we were able to fasten our WordPress site. We have now a simple WordPress advertising plugin that we can use to display the Ads. The next thing we have to do is to track the impressions, clicks and click-through-rate. This will be done in the next tutorial, so stay tuned!
If you had a hard time understanding any of the code above, please do say in the comments so that I can describe the code even better.
Become a Sponsor
Share this: